home *** CD-ROM | disk | FTP | other *** search
/ The CDPD Public Domain Collection for CDTV 3 / CDPDIII.bin / pd / amigamagazin / 11-92b / virtual memory / virtualmemory.mod < prev    next >
Text File  |  1992-08-29  |  32KB  |  1,178 lines

  1. $$LongAllign:=FALSE
  2. $$ConstChk:=FALSE
  3. MODULE VirtualMemory;
  4.  
  5. FROM Exec      IMPORT SuperState,UserState,CopyMemQuick,Forbid,Permit,Alert;
  6. FROM Resources IMPORT New,Dispose,Allocate;
  7. FROM System    IMPORT BITSET,Regs,PROC,LONGSET,SHORTSET;
  8.                IMPORT Dos;
  9.  
  10. |
  11. | Terminiere das Programm mit einem AutoRequest, der die Fehlerursache
  12. | anzeigt.
  13. |
  14. PROCEDURE Terminate(REF alert : LIST OF STRING);
  15. FROM Graphics  IMPORT TextAttr;
  16. FROM Intuition IMPORT AutoRequest,IntuiText;
  17. CONST
  18.   Topaz   = TextAttr:(name="topaz.font".data'PTR,ySize=11);
  19.   Ok      = IntuiText:(frontPen=2,leftEdge=5,topEdge=4,
  20.                        iTextFont=Topaz'PTR,
  21.                        iText="Give Up".data'PTR);
  22. VAR lines   : ARRAY [10] OF IntuiText;
  23.     i,j,x,w : INTEGER;
  24. BEGIN
  25.   w:=0;
  26.   FOR i:=0 TO alert'MAX DO
  27.     IF alert[i].len>w THEN w:=alert[i].len END
  28.   END;
  29.   w:=w*8+20;
  30.   FOR i:=0 TO alert'MAX DO
  31.     WITH lines[i] AS l DO
  32.       IF i#alert'MAX THEN
  33.         l.nextText:=lines[SUCC(i)]'PTR;
  34.       ELSE
  35.         l.nextText:=NIL;
  36.       END;
  37.       l.frontPen:=2;
  38.       l.iTextFont:=Topaz'PTR;
  39.       l.iText:=alert[i].data'PTR;
  40.       l.topEdge:=20+10*i;
  41.       l.leftEdge:=(w-alert[i].len*8) DIV 2;
  42.     END;
  43.   END;
  44.   FORGET AutoRequest(NIL,lines[0]'PTR,NIL,Ok'PTR,{},{},w,alert'RANGE*10+40);
  45.   HALT(10);
  46. END Terminate;
  47.  
  48. VAR
  49.   SwapFileName   : STRING(100);
  50.   PhysSize       : LONGINT;
  51.   SwapSize       : LONGINT;
  52.   LookAhead      : LONGINT := 8;
  53.   VMemBase       : LONGINT := $08000000;
  54.  
  55. TYPE
  56.   LongPtr        = POINTER TO LONGINT;
  57.  
  58. TYPE
  59.   MMURootFlags   = (dt0,dt1,lu=31);
  60.   MMURootFlagSet = SET OF MMURootFlags;
  61.   MMUTCFlags     = (tid0,tid1,tid2,tid3,
  62.                     tic0,tic1,tic2,tic3,
  63.                     tib0,tib1,tib2,tib3,
  64.                     tia0,tia1,tia2,tia3,
  65.                     is0,is1,is2,is3,
  66.                     ps0,ps1,ps2,ps3,
  67.                     fcl,sre,enable=31);
  68.   MMUTCFlagSet   = SET OF MMUTCFlags;
  69.  
  70.   MMUTransFlags  = (valid0,valid1,
  71.                     usedBefore,
  72.                     touched,
  73.                     modified,mmut5,
  74.                     cacheable,mmut6,mmut31 = 31);
  75.   MMUTransFlagSet= SET OF MMUTransFlags;
  76.   MMUTPtr        = POINTER TO MMUTransFlagSet;
  77.  
  78.   LevelAPtr      = POINTER TO ARRAY [256] OF LONGINT;
  79.   LevelBPtr      = POINTER TO ARRAY [32] OF LONGINT;
  80.   LevelCPtr      = POINTER TO ARRAY [64] OF MMUTransFlagSet;
  81.   LevelBCPtr     = POINTER TO ARRAY [32],[64] OF LONGINT;
  82.   LevelCCPtr     = POINTER TO ARRAY [2048] OF LONGINT;
  83.  
  84.   MMUConfig      = RECORD
  85.                      rootData : MMURootFlagSet;
  86.                      rootPos  : ANYPTR;
  87.                      tc       : MMUTCFlagSet;
  88.                      levelA   : LevelAPtr;
  89.                    END;
  90.  
  91.   VMemNodePtr    = POINTER TO VMemNode;
  92.   VMemNode       = RECORD
  93.                      next  : VMemNodePtr;
  94.                      pos,
  95.                      vpos  : LONGINT;
  96.                      mmu   : MMUTPtr;
  97.                      clear : BOOLEAN;
  98.                    END;
  99.   VMemMsgPtr     = POINTER TO VMemMsg;
  100.   VMemMsg        = RECORD OF Exec.Message;
  101.                      pos   : ANYPTR;
  102.                    END;
  103.  
  104.   ShortExFrame = POINTER TO RECORD
  105.                    regs    : ARRAY [15] OF LONGINT;
  106.                    status  : BITSET;
  107.                    pc      : ANYPTR;
  108.                    vector  : CARDINAL;
  109.                    res1    : CARDINAL;
  110.                    special : BITSET;
  111.                    res2,
  112.                    res3    : CARDINAL;
  113.                    fault   : ANYPTR;
  114.                  END;
  115.  
  116. CONST
  117.   NodeSize   = $2000;
  118.  
  119.   oldIllegal = ARRAY OF PROC:(NIL);
  120.   myA4       = ARRAY OF LONGINT:(0);
  121.  
  122.   MemSem     = Exec.SignalSemaphore:();
  123.  
  124. VAR
  125.   LastMem      : VMemNodePtr;
  126.   ServerPort   : Exec.MsgPort;
  127.  
  128.   VBR          : LONGINT;
  129.  
  130.   lowMem       : LONGINT;
  131.   highMem      : LONGINT;
  132.  
  133.   SysMMU       : MMUConfig;
  134.   OwnMMU       : MMUConfig;
  135.  
  136.   Thrashing    : BOOLEAN;
  137.  
  138.   isA3000      : BOOLEAN;
  139.  
  140.   mem          : Exec.MemHeaderPtr;
  141.  
  142. DEFINITION MODULE DiskIO;
  143. |
  144. | IO Routinenen, die die Unterschiede zwischen einem File und einer Partition
  145. | vor einem übergeordneten Benutzer verbergen.
  146. |
  147.  
  148.   PROCEDURE Open(REF name : STRING;VAR size : LONGINT);
  149.  
  150.   PROCEDURE Close;
  151.  
  152.   PROCEDURE Read(dst : ANYPTR;pos,len : LONGINT);
  153.  
  154.   PROCEDURE Write(src : ANYPTR;pos,len : LONGINT);
  155.  
  156.   PROCEDURE MotorOff;
  157.  
  158. END DiskIO;
  159.  
  160. IMPLEMENTATION MODULE DiskIO;
  161.  
  162.   FROM Resources IMPORT New,Dispose,Allocate;
  163.   FROM System    IMPORT Regs;
  164.   FROM Dos       IMPORT DeviceListPtr;
  165.   FROM Exec      IMPORT IOStdReq,DoIO,read,write;
  166.   FROM Trackdisk IMPORT IOTrackDiskPtr;
  167.                  IMPORT Strings;
  168.   FROM Str       IMPORT CAP;
  169.  
  170.   VAR SwapFile  : Dos.FileHandlePtr;
  171.       SwapPos   : LONGINT;
  172.       Device    : IOTrackDiskPtr;
  173.       sigBit    : INTEGER;
  174.       port      : Exec.MsgPortPtr;
  175.       BlockSize : INTEGER;
  176.       Base      : LONGINT;
  177.       Size      : LONGINT;
  178.  
  179.   PROCEDURE Open(REF name : STRING;VAR size : LONGINT);
  180.   VAR i,oldLen  : LONGINT;
  181.       buffer    : POINTER TO ARRAY [16*NodeSize] OF SHORTCARD;
  182.       dev       : DeviceListPtr;
  183.       name2     : STRING(40);
  184.       bpcyl     : LONGINT;
  185.       root      : LONGINT;
  186.       RootBlock : POINTER TO ARRAY OF LONGINT;
  187.       blkSize   : LONGINT;
  188.       ee        : POINTER TO Dos.DosEnvec;
  189.  
  190.     PROCEDURE FindDevEntry(REF name : STRING):DeviceListPtr;
  191.     VAR p : DeviceListPtr;
  192.  
  193.       PROCEDURE Equal(new : Dos.BSTR):BOOLEAN;
  194.       VAR s IN A0,d IN A1 : POINTER TO CHAR;
  195.       BEGIN
  196.         IF new#NIL THEN
  197.           s:=new^'PTR;
  198.           d:=name.data[0]'PTR;
  199.           IF name.len=INTEGER(s+^) THEN
  200.             LOOP
  201.               IF KEY CAP[d+^]
  202.                 OF &0       THEN RETURN TRUE END
  203.                 OF CAP[s+^] THEN             END
  204.               ELSE
  205.                 RETURN FALSE
  206.               END;
  207.             END;
  208.           ELSE
  209.             RETURN FALSE
  210.           END;
  211.         ELSE
  212.           RETURN FALSE
  213.         END;
  214.       END Equal;
  215.  
  216.     BEGIN
  217.       Exec.Forbid;
  218.       p:=Dos.DosBase.root.info^.devInfo;
  219.       WHILE (p#NIL) AND
  220.             ((p^.type#Dos.device) OR NOT Equal(p^.name)) DO p:=p^.next END;
  221.       Exec.Permit;
  222.       RETURN p;
  223.     END FindDevEntry;
  224.  
  225.   BEGIN
  226.     IF name.data[name.len-1]=":" THEN
  227.       name2:=name;DEC(name2.len);name2.data[name2.len]:=&0;
  228.       dev:=FindDevEntry(name2);
  229.       IF (dev#NIL) AND (dev^.startup#NIL) AND (dev^.startup^.environ#NIL) THEN
  230.         ee:=dev^.startup^.environ^'PTR;
  231.         WITH ee^ AS e DO
  232.           blkSize:=e.sizeBlock;
  233.           bpcyl:=e.sizeBlock*e.surfaces*e.blocksPerTrack*4;
  234.           Base:=LONGINT(e.lowCyl)*bpcyl;
  235.           Size:=LONGINT(e.highCyl+1-e.lowCyl)*bpcyl;
  236.           root:=((e.highCyl+1+e.lowCyl)*e.blocksPerTrack*e.surfaces+1) DIV 2;
  237.         END;
  238.         IF size=0 THEN size:=Size DIV (16*NodeSize) * (16*NodeSize) END;
  239.         IF Size>=size THEN
  240.           Exec.OpenDevice(Strings.BSTRtoString(dev^.startup^.device),dev^.startup^.unit,Device^,{});
  241.           |Exec.OpenDevice("trackdisk.device",0,Device^,{});
  242.           IF Device.error=0 THEN
  243.             Allocate(RootBlock,blkSize*4);
  244.             Device.command:=Exec.read;
  245.             Device.length:=blkSize*4;
  246.             Device.data:=RootBlock;
  247.             Device.offset:=Base;
  248.             Exec.DoIO(Device);
  249.             IF Device.error=0 THEN
  250.               IF RootBlock[0] OF $444F5300,$444F5301 THEN
  251.                 Device.command:=Exec.read;
  252.                 Device.length:=blkSize*4;
  253.                 Device.data:=RootBlock;
  254.                 Device.offset:=root*blkSize*4;
  255.                 Exec.DoIO(Device);
  256.                 IF Device.error=0 THEN
  257.                   IF (RootBlock[0]=2) AND (RootBlock[blkSize-1]=1) THEN
  258.                     FOR i:=6 TO 5+RootBlock[3] DO
  259.                       IF RootBlock[i]#0 THEN
  260.                         Exec.CloseDevice(Device^);
  261.                         Device.device:=NIL;
  262.                         Dispose(RootBlock);
  263.                         Terminate("Partition error:",
  264.                                 "Partition is not empty",
  265.                                 "Please delete all files");
  266.                       END;
  267.                     END;
  268.                   END;
  269.                 END;
  270.               END;
  271.               Dispose(RootBlock);
  272.             ELSE
  273.               Exec.CloseDevice(Device^);
  274.               Device.device:=NIL;
  275.               Dispose(RootBlock);
  276.               Terminate("Partition error:",
  277.                       "Read error");
  278.             END;
  279.           ELSE
  280.             Terminate("Partition error:",
  281.                     "Read error");
  282.           END;
  283.         ELSE
  284.           Terminate("Partition error:",
  285.                   "Partition too small");
  286.         END;
  287.       ELSE
  288.         Terminate("Partition error:",
  289.                   "Partition does not exist",
  290.                   "Use physical name !!");
  291.       END;
  292.     ELSE
  293.       IF size=0 THEN
  294.         Terminate("Swapfile error:",
  295.                 "Size 0 can not be used",
  296.                 "for file swapping",
  297.                 "Use 'FILESIZE=...'",
  298.                 "In blocks of 128KBytes");
  299.       END;
  300.       SwapFile:=Dos.Open(name,Dos.readWrite);
  301.       IF SwapFile#NIL THEN
  302.         Size:=size;
  303.         New(buffer);
  304.         FOR i:=0 TO 16*NodeSize-1 DO
  305.           buffer[i]:=0
  306.         END;
  307.         FORGET Dos.Seek(SwapFile,0,Dos.end);
  308.         oldLen:=Dos.Seek(SwapFile,0,Dos.current);
  309.         FOR i:=oldLen DIV (16*NodeSize) TO Size DIV (16*NodeSize) -1 DO
  310.           IF Dos.Write(SwapFile,buffer,16*NodeSize)#16*NodeSize THEN
  311.             Terminate("Swapfile error:",
  312.                     "Not enough disk space");
  313.           END;
  314.         END;
  315.         SwapPos:=0;
  316.         FORGET Dos.Seek(SwapFile,0,Dos.beginning);
  317.         Dispose(buffer);
  318.       ELSE
  319.         Terminate("Swapfile error:",
  320.                 "could not open Swapfile");
  321.       END;
  322.     END;
  323.   END Open;
  324.  
  325.   PROCEDURE Close;
  326.   BEGIN
  327.     IF SwapFile#NIL THEN
  328.       Dos.Close(SwapFile)
  329.     ELSE
  330.       Exec.CloseDevice(Device^);
  331.     END;
  332.   END Close;
  333.  
  334.   PROCEDURE Read(dst : ANYPTR;pos,len : LONGINT);
  335.   BEGIN
  336.     IF pos OF 0..Size-1 THEN
  337.       IF SwapFile#NIL THEN
  338.         FORGET Dos.Seek(SwapFile,pos-SwapPos,Dos.current);
  339.         FORGET Dos.Read(SwapFile,dst,len);
  340.         SwapPos:=pos+len;
  341.       ELSE
  342.         Device.command:=Exec.read;
  343.         Device.length:=len;
  344.         Device.data:=dst;
  345.         Device.offset:=pos+Base;
  346.         Exec.DoIO(Device);
  347.       END;
  348.     END;
  349.   END Read;
  350.  
  351.   PROCEDURE Write(src : ANYPTR;pos,len : LONGINT);
  352.   BEGIN
  353.     IF pos OF 0..Size-1 THEN
  354.       IF SwapFile#NIL THEN
  355.         FORGET Dos.Seek(SwapFile,pos-SwapPos,Dos.current);
  356.         FORGET Dos.Write(SwapFile,src,len);
  357.         SwapPos:=pos+len;
  358.       ELSE
  359.         Device.command:=Exec.write;
  360.         Device.length:=len;
  361.         Device.data:=src;
  362.         Device.offset:=pos+Base;
  363.         Exec.DoIO(Device);
  364.       END;
  365.     END;
  366.   END Write;
  367.  
  368.   PROCEDURE MotorOff;
  369.   BEGIN
  370.     IF SwapFile=NIL THEN
  371.       Device.command:=Trackdisk.motor;
  372.       Device.length:=0;
  373.       Exec.DoIO(Device);
  374.     END;
  375.   END MotorOff;
  376.  
  377. BEGIN
  378.   sigBit:=Exec.AllocSignal(-1);
  379.   New(port,clear:=TRUE);
  380.   New(Device,clear:=TRUE);
  381.   port.name:=NIL;
  382.   port.pri:=0;
  383.   port.type:=Exec.msgPort;
  384.   port.flags:=Exec.signal;
  385.   port.sigBit:=sigBit;
  386.   port.sigTask:=Exec.FindTask(NIL);
  387.   Exec.NewList(port.msgList,Exec.message);
  388.   Device.type:=Exec.message;
  389.   Device.replyPort:=port;
  390. CLOSE
  391.   IF SwapFile#NIL THEN
  392.     Dos.Close(SwapFile);SwapFile:=NIL;
  393.   END;
  394.   IF Device.device#NIL THEN
  395.     Exec.CloseDevice(Device^);
  396.     Device.device:=NIL
  397.   END;
  398.   Exec.FreeSignal(sigBit);
  399. END DiskIO;
  400.  
  401. |
  402. | GetVBR:
  403. |
  404. |  Inhalt des Vector Base Registers ermitteln, da dieses die Basisadresse der
  405. |  Exceptionvektoren enthält.
  406. |
  407. PROCEDURE GetVBR():LONGINT;
  408. BEGIN
  409.   ASSEMBLE(MOVE.L $4,A6
  410.            JSR    -$96(A6)            | SuperState
  411.            DC.W   $4E7A,$2801         | MOVEC VBR,D2
  412.            JSR    -$9C(A6)            | UserState
  413.            MOVE.L D2,D0               | Returnwert.
  414.           );
  415. END GetVBR;
  416.  
  417. |
  418. | GetMMUConfig:
  419. |
  420. |   Aktuellen Zustand der MMU ermitteln
  421. |
  422. PROCEDURE GetMMUConfig(VAR con IN A2 : MMUConfig);
  423. BEGIN
  424.   ASSEMBLE(MOVE.L $4,A6
  425.            JSR    -$96(A6)            | SuperState
  426.            DC.W   $F012,$4E00         | PMOVE CRP,(A2)
  427.            DC.W   $F02A,$4200,$0008   | PMOVE TC,8(A2)
  428.            JSR    -$9C(A6)            | UserState
  429.           );
  430. END GetMMUConfig;
  431.  
  432. |
  433. | PutMMUConfig:
  434. |
  435. |   Zustand der MMU ändern
  436. |
  437. PROCEDURE PutMMUConfig(REF con IN A2 : MMUConfig);
  438. BEGIN
  439.   ASSEMBLE(MOVE.L $4,A6
  440.            JSR    -120(A6)
  441.            JSR    -$96(A6)          | SuperState
  442.            DC.W   $42A7,$F017
  443.            DC.W   $4000,$588F       | CLR   TC
  444.            DC.W   $F012,$4C00       | PMOVE CRP,(A2)
  445.            DC.W   $F02A,$4000,$0008 | PMOVE TC,8(A2)
  446.            JSR    -$9C(A6)          | UserState
  447.            JSR    -126(A6)
  448.           );
  449. END PutMMUConfig;
  450.  
  451. |
  452. | InvalidateMMUCache:
  453. |
  454. |   Seitendeskriptor im MMU cache invalidieren, ist unbedingt nötig, wenn
  455. |   dieser geändert wurde, da nur so die Änderung in die MMU übernommen wird.
  456. |   Alternativ könnte auch der PFLUSHA verwendet werden, der allerdings alle
  457. |   MMU cache Einträge invalidiert.
  458. |
  459. PROCEDURE InvalidateMMUCache(pos : LONGINT);
  460. BEGIN
  461.   ASSEMBLE(MOVE.L $4,A6
  462.            JSR    -$96(A6)      | SuperState
  463.            MOVE.L pos,A0
  464.            DC.W   $F010,$3810   | PFLUSH #0,#0,(A0)
  465.            JSR    -$9C(A6)      | UserState
  466.           );
  467. END InvalidateMMUCache;
  468.  
  469. |
  470. | New16:
  471. |
  472. |   Ein Speicherblock auf 16Byte Grenze Allozieren, nötig, da alle MMU Elemente
  473. |   auf 16 byte allignment angewiesen sind.
  474. |
  475. PROCEDURE New16(VAR a : ANYPTR);
  476. BEGIN
  477.   Allocate(a,a'SIZE+16);
  478.   IF LONGINT(a) MOD 16#0 THEN INC(LONGINT(a),8) END;
  479. END New16;
  480.  
  481. |
  482. | CreateMMUTree:
  483. |
  484. |  Standard MMU Baum erzeugen.
  485. |
  486. |   Zu Berücksichtigen sind:
  487. |
  488. |     Chip Memory
  489. |     Kickstart rom auf A3000
  490. |     IO Bereich
  491. |
  492. |   Caching bräuchte allerdings nicht durch die MMU realsisert werden (zu
  493. |   mindest nicht mit '030, da das caching auf diesem Prozessor auch durch
  494. |   externe Hardware unterbunden werden kann. Auf Prozessoren mit einem
  495. |   CopyBack modus ist dies nicht möglich, da eventuell für ein zu schreibendes
  496. |   Datenwort überhaupt kein Speicherzugriff erfolgt.
  497. |
  498. PROCEDURE CreateMMUTree(VAR con : MMUConfig);
  499. VAR levelA : LevelAPtr;
  500.     levelB : LevelBPtr;
  501.     i      : INTEGER;
  502. BEGIN
  503.   con.tc:={enable,          | MMU ist aktiv
  504.            ps3,ps2,ps0,     | Page size 8 K (könnte evtl. Parameter werden)
  505.            tia3,            | 8 Bits level A
  506.            tib2,tib0,       | 5 Bits level B
  507.            tic2,tic1        | 6 Bits level C
  508.            };               | + 13 Bits rest => 32 Bits physical
  509.  
  510.   |
  511.   | Root mit early termination, Addresse und DT = $1 für PageDescriptor
  512.   |
  513.   New16(levelA);
  514.   con.levelA:=levelA;
  515.   FOR i:=0 TO 255 DO
  516.     levelA[i]:=LONGINT(i) SHL 24+%00000001;
  517.   END;
  518.  
  519.   |
  520.   | Level B, für Chip mem und evtl. Rom remapping
  521.   |
  522.   New16(levelB);
  523.   levelA[$00]:=LONGINT(levelB)+%10;
  524.   FOR i:=0 TO 3 DO
  525.     levelB[i]:=$00000000+LONGINT(i) SHL 19+%01000001;
  526.   END;
  527.   FOR i:=4 TO 30 DO
  528.     levelB[i]:=$00000000+LONGINT(i) SHL 19+%00000001;
  529.   END;
  530.  
  531.   |
  532.   | für A3000 muß das Kickstart mit der MMU evtl. remapped werden
  533.   |
  534.   IF isA3000 THEN
  535.     levelB[31]:=$07F80000+%00000101;
  536.     New16(levelB);
  537.     levelA[$07]:=LONGINT(levelB)+%10;
  538.     FOR i:=0 TO 30 DO
  539.       levelB[i]:=$07000000+LONGINT(i) SHL 19+%00000001;
  540.     END;
  541.     levelB[31]:=$07F80000+%00000101;
  542.   END;
  543.  
  544.   |
  545.   | levelA zeiger eintragen, Flags für Short descriptoren
  546.   |
  547.   con.rootPos:=levelA;
  548.   con.rootData:={lu,dt1};
  549. END CreateMMUTree;
  550.  
  551. |
  552. | CreateVMemArea:
  553. |
  554. |   Virtuellen Bereich im MMU Baum eintragen
  555. |
  556. PROCEDURE CreateVMemArea(VAR con : MMUConfig;from,to : ANYPTR);
  557. VAR levelB : LevelBPtr;
  558.     levelC : LevelBCPtr;
  559.     i,j,k  : INTEGER;
  560. BEGIN
  561.   FOR k:=LONGINT(from) SHR 24 TO (to-1) SHR 24 DO
  562.     New16(levelB);
  563.     New16(levelC);
  564.     FOR i:=0 TO 31 DO
  565.       levelB[i]:=LONGINT(levelC[i]'PTR)+%00000010;
  566.       FOR j:=0 TO 63 DO
  567.         levelC[i,j]:=0;
  568.       END;
  569.     END;
  570.     con.levelA[k]:=LONGINT(levelB)+%00000010;
  571.   END;
  572.   lowMem:=from;
  573.   highMem:=to;
  574. END CreateVMemArea;
  575.  
  576. |
  577. | AddBuffers:
  578. |
  579. |   Pufferspeicher für eingelagerte Seiten anfordern und den Bestehenden
  580. |   hinzufügen.
  581. |
  582. PROCEDURE AddBuffers(buff : INTEGER);
  583. VAR mem  : ANYPTR;
  584.     node : VMemNodePtr;
  585.     i    : INTEGER;
  586. BEGIN
  587.   Allocate(mem,LMUL(buff+1,NodeSize));
  588.   mem:=(LONGINT(mem)+NodeSize-1) DIV NodeSize * NodeSize;
  589.   FOR i:=1 TO buff DO
  590.     New(node);
  591.     node.pos:=mem;
  592.     node.mmu:=NIL;
  593.     node.clear:=FALSE;
  594.     Forbid;
  595.     IF LastMem=NIL THEN
  596.       LastMem:=node;
  597.       node.next:=node
  598.     ELSE
  599.       node.next:=LastMem.next;
  600.       LastMem.next:=node;
  601.     END;
  602.     Permit;
  603.     INC(mem,NodeSize);
  604.   END;
  605. END AddBuffers;
  606.  
  607. |
  608. | NextVMemNode:
  609. |
  610. |   Nächste zu verwendende Seite im physikalischen Speicher ermitteln. Die
  611. |   Routine verwendet einen second chance algorithmus, der einem echten LRU
  612. |   sehr nahe kommt, diesem aber an Leistung weit überlegen ist.
  613. |
  614. PROCEDURE NextMemNode():VMemNodePtr;
  615. VAR p IN A0 : VMemNodePtr;
  616. BEGIN
  617.   p:=LastMem;
  618.   WHILE (p.mmu#NIL) AND (touched IN p.mmu^) DO
  619.     EXCL(p.mmu^,touched);
  620.     p:=p.next
  621.   END;
  622.   LastMem:=p.next;
  623.   RETURN p
  624. END NextMemNode;
  625.  
  626. |
  627. | InvalidateNode:
  628. |
  629. |   Eine Seite invalidieren, also aus der Menge erreichbaren Seiten entfernen.
  630. |   Falls die Seite veränder worden ist, muß sie danach auf die Disk gesichert
  631. |   werden.
  632. |
  633. PROCEDURE InvalidateNode(node : VMemNodePtr);
  634. BEGIN
  635.   IF node.mmu#NIL THEN
  636.     EXCL(node.mmu^,valid0);
  637.     InvalidateMMUCache(node.vpos);
  638.     IF modified IN node.mmu^ THEN
  639.       node.clear:=FALSE;
  640.       DiskIO.Write(node.pos,node.vpos-lowMem,NodeSize);
  641.       INCL(node.mmu^,usedBefore);
  642.       Thrashing:=TRUE;
  643.     OR_IF NOT node.clear THEN
  644.       INCL(node.mmu^,usedBefore);
  645.     END;
  646.     node.mmu:=NIL;
  647.   END;
  648. END InvalidateNode;
  649.  
  650. |
  651. | FlushNode:
  652. |
  653. |   Eine Seite, falls diese verändert wurde, auf die Disk sichern, und wieder
  654. |   als unverändert markieren.
  655. |
  656. PROCEDURE FlushNode(node : VMemNodePtr);
  657. BEGIN
  658.   IF (node.mmu#NIL) AND (modified IN node.mmu^) THEN
  659.     EXCL(node.mmu^,modified);
  660.     InvalidateMMUCache(node.vpos);
  661.     node.clear:=FALSE;
  662.     DiskIO.Write(node.pos,node.vpos-lowMem,NodeSize);
  663.   END;
  664. END FlushNode;
  665.  
  666. |
  667. | FlushNodes:
  668. |
  669. |   Eine vorgegebene Anzahl von Seiten für eine evtl. spätere Benutzung
  670. |   freiräumen, also falls diese verändert wurden auf die Disk sichern.
  671. |
  672. |   Dieses speichern in größeren Stückzahlen hat den Vorteil, daß es
  673. |   optimiert, also in der richtigen Reihenfolge (Kopfbewegungsoptimierung)
  674. |   ausgeführt werden kann.
  675. |
  676. PROCEDURE FlushNodes(n : INTEGER);
  677. VAR buff  : ARRAY [256] OF VMemNodePtr;
  678.     num,i : INTEGER;
  679.     j     : INTEGER;
  680.     p     : VMemNodePtr;
  681. BEGIN
  682.   p:=LastMem;
  683.   num:=0;
  684.   FOR i:=1 TO n DO
  685.     IF (p.mmu#NIL) AND (touched NOT IN p.mmu^) AND (modified IN p.mmu^) THEN
  686.       j:=num;
  687.       WHILE (j>0) AND (buff[j-1].vpos>p.vpos) DO
  688.         buff[j]:=buff[j-1];
  689.         DEC(j)
  690.       END;
  691.       buff[j]:=p;
  692.       INC(num);
  693.     END;
  694.     p:=p.next;
  695.   END;
  696.   IF num>0 THEN
  697.     FOR i:=0 TO num-1 DO
  698.       FlushNode(buff[i]);
  699.     END;
  700.   END;
  701. END FlushNodes;
  702.  
  703. |
  704. | ClearMem:
  705. |
  706. |   Speicherbereich mit Nullen auffüllen.
  707. |
  708. PROCEDURE ClearMem(pos : ANYPTR;size : LONGINT);
  709. VAR p IN A0 : POINTER TO LONGINT;
  710.     i IN D1,
  711.     j IN D2 : LONGINT;
  712. BEGIN
  713.   p:=pos;
  714.   j:=0;
  715.   FOR i:=size DIV 4-1 TO 0 BY -1 DO
  716.     p+^:=j
  717.   END;
  718. END ClearMem;
  719.  
  720. |
  721. | ReloadNode:
  722. |
  723. |   Eine verdrängte oder noch nicht benutzte Seite dem System zugängig machen.
  724. |   Dies kann entweder durch einladen von Disk, oder durch Löschen (falls die
  725. |   Seite noch nicht benutzt wurde) geschehen.
  726. |
  727. PROCEDURE ReloadNode(node : VMemNodePtr;pos : LONGINT;mmu : MMUTPtr);
  728. BEGIN
  729.   node.mmu:=mmu;
  730.   node.vpos:=pos;
  731.   IF usedBefore IN mmu^ THEN
  732.     node.clear:=FALSE;
  733.     DiskIO.Read(node.pos,node.vpos-lowMem,NodeSize);
  734.   ELSE
  735.     IF NOT node.clear THEN
  736.       ClearMem(node.pos,NodeSize);
  737.       node.clear:=TRUE;
  738.     END;
  739.   END;
  740.   mmu^:=CAST(MMUTransFlagSet,node.pos)+{valid0,touched};
  741.   InvalidateMMUCache(node.vpos);
  742. END ReloadNode;
  743.  
  744. |
  745. | SwapMemIn:
  746. |
  747. |    Sorgt dafür, daß die angegebene Adresse dem System zugängig gemacht
  748. |    wird. Diese Funktion führt eventuell zu Disketten Zugriffen.
  749. |
  750. PROCEDURE SwapMemIn(pos : LONGINT);
  751. VAR mmu  : MMUTPtr;
  752.     mem  : LONGINT;
  753.     node : VMemNodePtr;
  754. BEGIN
  755.   mem:=pos DIV NodeSize * NodeSize;
  756.   mmu:=LevelCPtr(
  757.        LevelBPtr(OwnMMU.levelA[mem SHR 24 MOD 256] SHR 4 SHL 4)
  758.                               [mem SHR 19 MOD 32] SHR 4 SHL 4)
  759.                               [mem SHR 13 MOD 64]'PTR;
  760.   IF valid0 NOT IN mmu^ THEN
  761.     node:=NextMemNode();
  762.     InvalidateNode(node);
  763.     ReloadNode(node,mem,mmu);
  764.   END;
  765. END SwapMemIn;
  766.  
  767. |
  768. | Server:
  769. |
  770. |    Hauptschleife des Virtuell Memory Managers. Bekommt Anforderungen von
  771. |    Prozessen/Tasks, die einen gewünschten Speicherbereich nicht erreichen
  772. |    konnten, und deshalb in einer BusError Exception gelandet sind.
  773. |
  774. PROCEDURE Server;
  775. VAR msg : VMemMsgPtr;
  776. BEGIN
  777.   LOOP
  778.     Thrashing:=FALSE;
  779.     msg:=Exec.WaitPort(ServerPort'PTR);
  780.     msg:=Exec.GetMsg(ServerPort'PTR);
  781.     WHILE msg#NIL DO
  782.       SwapMemIn(msg.pos);
  783.       Exec.ReplyMsg(msg);
  784.       msg:=Exec.GetMsg(ServerPort'PTR);
  785.     END;
  786.     IF Thrashing THEN
  787.       FlushNodes(LookAhead);
  788.     END;
  789.   END;
  790. END Server;
  791.  
  792. |
  793. | MMUCall:
  794. |
  795. |   High level Routine des BusError Exceptionhandlers. Erzeugt einen Port,
  796. |   und richtet eine Anforderung an den Swapper process.
  797. |
  798. |   Die Einrichtung eines Ports ist etwas fragwürdig, doch kann aus
  799. |   Verständlichen Gründen in dieser Routine kein Speicher angefordert werden.
  800. |   Da Stack Speicher aber sowieso im Public memory liegen sollte, sollte dies
  801. |   keine Probleme machen.
  802. |
  803. PROCEDURE MMUCall(frame : ShortExFrame);
  804. VAR task : Exec.TaskPtr;
  805.     msg  : VMemMsg;
  806.     port : Exec.MsgPort;
  807. BEGIN
  808.   IF LONGINT(frame.fault) OF lowMem..highMem-1 THEN
  809.     port.flags:=Exec.signal;
  810.     port.sigBit:=Exec.AllocSignal(-1);
  811.     port.sigTask:=Exec.FindTask(NIL);
  812.     port.msgList.head:=Exec.NodePtr(port.msgList.tail'PTR);
  813.     port.msgList.tail:=NIL;
  814.     port.msgList.tailPred:=Exec.NodePtr(port.msgList.head'PTR);
  815.     msg.replyPort:=port'PTR;
  816.     msg.pos:=frame.fault;
  817.     Exec.PutMsg(ServerPort'PTR,msg'PTR);
  818.     REPEAT
  819.       FORGET Exec.WaitPort(port'PTR);
  820.     UNTIL Exec.GetMsg(port'PTR)#NIL;
  821.     Exec.FreeSignal(port.sigBit);
  822.   ELSE
  823.     EXCL(frame.special,8);
  824.   END;
  825. END MMUCall;
  826.  
  827. |
  828. | BusError:
  829. |
  830. |    Low level BusError Exception Handler, sichert den Exception frame
  831. |    auf dem user stack, schaltet in user mode zurück und ruft MMUCall
  832. |    auf.
  833. |
  834. PROCEDURE BusError;
  835. BEGIN
  836.   ASSEMBLE(
  837.            |
  838.            | Register sichern, und Variablenbasis holen
  839.            |
  840.            MOVE.L D0-D7/A0-A6,-(A7)
  841.            MOVE.L myA4[0],A4
  842.            |
  843.            | Feststellen ob short oder long frame
  844.            |
  845.            DC.W   $4E68               | MOVE.L USP,A0
  846.            BTST   #4,{$6+60}(A7)      | Test auf Typ des Frames
  847.            BNE    lFrame
  848.            |
  849.            | ShortFrame:
  850.            |
  851.            | Exception frame auf user stack kopieren
  852.            |
  853.            MOVE.L #30+16-1,D0
  854.            SUB.L  #60+32,A0
  855.            DC.W   $4E60               | MOVE.L A0,USP
  856.            MOVE.L A7,A1
  857.   sLoop:   MOVE.W (A1)+,(A0)+
  858.            DBRA   D0,sLoop
  859.            ADD.L  #60+32,A7
  860.            |
  861.            | In user mode zurück schalten
  862.            |
  863.            DC.W   $027C,$DFFF         | Back to user Mode
  864.            |
  865.            | High level Routine aufrufen
  866.            |
  867.            PEA    (A7)
  868.            JSR    MMUCall
  869.            |
  870.            | Zurück in supervisor mode
  871.            |
  872.            MOVE.L 4,A6
  873.            JSR    -$96(A6)            | Supervisor
  874.            |
  875.            | Exception frame wieder auf Supervisor Stack
  876.            |
  877.            MOVE.L A7,A0               | User Stack
  878.            MOVE.L D0,A7               | Supervisorstack
  879.            SUB.L  #60+32,A7
  880.            MOVE.L A7,A1
  881.            MOVE.L #30+16-1,D0
  882.   sLoop2:  MOVE.W (A0)+,(A1)+
  883.            DBRA   D0,sLoop2
  884.            DC.W   $4E60               | MOVE.L A0,USP
  885.            |
  886.            | Und raus hier
  887.            |
  888.            BRA    exit
  889.            |
  890.            | LongFrame:
  891.            |
  892.            | Exception frame auf user stack kopieren
  893.            |
  894.   lFrame:  MOVE.L #30+46-1,D0
  895.            SUB.L  #60+92,A0
  896.            DC.W   $4E60               | MOVE.L A0,USP
  897.            MOVE.L A7,A1
  898.   lLoop:   MOVE.W (A1)+,(A0)+
  899.            DBRA   D0,lLoop
  900.            ADD.L  #60+92,A7
  901.            |
  902.            | In user mode zurück schalten
  903.            |
  904.            DC.W   $027C,$DFFF         | Back to user Mode
  905.            |
  906.            | High level routine aufrufen
  907.            |
  908.            PEA    (A7)
  909.            JSR    MMUCall
  910.            |
  911.            | Zurück in Supervisor mode
  912.            |
  913.            MOVE.L 4,A6
  914.            JSR    -$96(A6)            | Supervisor
  915.            |
  916.            | Exception frame wieder auf Supervisor Stack
  917.            |
  918.            MOVE.L A7,A0               | User Stack
  919.            MOVE.L D0,A7               | Supervisorstack
  920.            SUB.L  #60+92,A7
  921.            MOVE.L A7,A1
  922.            MOVE.L #30+46-1,D0
  923.   lLoop2:  MOVE.W (A0)+,(A1)+
  924.            DBRA   D0,lLoop2
  925.            DC.W   $4E60               | MOVE.L A0,USP
  926.            |
  927.            | Register zurück holen und Exception beenden
  928.            |
  929.   exit:    MOVE.L (A7)+,D0-D7/A0-A6
  930.            RTE);
  931. END BusError;
  932.  
  933.  
  934. TYPE ProcPtr = POINTER TO PROC;
  935.  
  936. TYPE ShortPtr = POINTER TO SHORTSET;
  937.  
  938. CONST OldAllocMem = ARRAY OF PROC:(NIL);
  939.       OldFreeMem  = ARRAY OF PROC:(NIL);
  940.       OldAvailMem = ARRAY OF PROC:(NIL);
  941.  
  942. |
  943. | SaveAllocMem, FreeMem, AvailMem:
  944. |
  945. |    Erweiterung der AllocMem.. Funktion um eine Semaphore. Ein einfaches Forbid
  946. |    reicht nich mehr, da der anfordernde Task ja durch eine eventuelle Swap
  947. |    Operation unterbrochen werden könnte, und dann die Speicherliste bei
  948. |    gleichzeitiger Anforderung durch einen anderen Task beim Teufel wäre.
  949. |
  950. PROCEDURE SaveAllocMem;
  951. BEGIN
  952.   ASSEMBLE(MOVE.L D0-D1,-(A7)
  953.            LEA    MemSem,A0
  954.            JSR    -564(A6)
  955.            MOVE.L (A7)+,D0-D1
  956.            MOVE.L OldAllocMem,A0
  957.            JSR    (A0)
  958.            MOVE.L D0,-(A7)
  959.            LEA    MemSem,A0
  960.            JSR    -570(A6)
  961.            MOVE.L (A7)+,D0
  962.            RTS);
  963. END SaveAllocMem;
  964.  
  965. PROCEDURE SaveFreeMem;
  966. BEGIN
  967.   ASSEMBLE(MOVE.L D0/A1,-(A7)
  968.            LEA    MemSem,A0
  969.            JSR    -564(A6)
  970.            MOVE.L (A7)+,D0/A1
  971.            MOVE.L OldFreeMem,A0
  972.            JSR    (A0)
  973.            MOVE.L D0,-(A7)
  974.            LEA    MemSem,A0
  975.            JSR    -570(A6)
  976.            MOVE.L (A7)+,D0
  977.            RTS);
  978. END SaveFreeMem;
  979.  
  980. PROCEDURE SaveAvailMem;
  981. BEGIN
  982.   ASSEMBLE(MOVE.L D1,-(A7)
  983.            LEA    MemSem,A0
  984.            JSR    -564(A6)
  985.            MOVE.L (A7)+,D1
  986.            MOVE.L OldAvailMem,A0
  987.            JSR    (A0)
  988.            MOVE.L D0,-(A7)
  989.            LEA    MemSem,A0
  990.            JSR    -570(A6)
  991.            MOVE.L (A7)+,D0
  992.            RTS);
  993. END SaveAvailMem;
  994.  
  995. LIBRARY Exec.ExecBase BY -636 PROCEDURE CacheClearU();
  996.  
  997. |
  998. | GetStartupParams:
  999. |
  1000. |    Parameter des Swappers aus dem Icon ermitteln.
  1001. |
  1002. PROCEDURE GetStartupParams;
  1003. FROM Strings     IMPORT Str;
  1004. FROM Dos         IMPORT FileLockPtr,CurrentDir;
  1005. FROM Icon        IMPORT FreeDiskObject,FindToolType;
  1006. FROM Workbench   IMPORT DiskObjectPtr,StartupMsg;
  1007. FROM Conversions IMPORT HexStringToInt,StringToInt,IntToHexString;
  1008.  
  1009. VAR lock    : FileLockPtr;
  1010.     ptr     : System.SysStringPtr;
  1011.     obj     : DiskObjectPtr;
  1012.  
  1013. LIBRARY Icon.IconBase BY -78  PROCEDURE GetDiskObject(Name IN A0 : ANYPTR):DiskObjectPtr;
  1014.  
  1015. BEGIN
  1016.   IF StartupMsg#NIL THEN
  1017.     lock:=CurrentDir(StartupMsg.argList[0].lock);
  1018.     obj:=GetDiskObject(StartupMsg.argList[0].name);
  1019.     FORGET CurrentDir(lock);
  1020.     IF obj=NIL THEN
  1021.       Terminate("Could not find icon");
  1022.     END;
  1023.  
  1024.     ptr:=FindToolType(obj.toolTypes,"SWAPFILE".data'PTR);
  1025.     IF ptr=NIL THEN
  1026.       Terminate("Parameter error:",
  1027.                 "No Swapfile/Partition defined",
  1028.                 "Use 'SWAPFILE=...'");
  1029.     END;
  1030.     SwapFileName:=Str(ptr);
  1031.  
  1032.     ptr:=FindToolType(obj.toolTypes,"SWAPBUFFER".data'PTR);
  1033.     IF ptr=NIL THEN
  1034.       Terminate("Parameter error:",
  1035.                 "No Swapbuffer defined",
  1036.                 "Use 'SWAPBUFFER=...'",
  1037.                 "In blocks of 8KBytes");
  1038.     END;
  1039.     TRY
  1040.       PhysSize:=NodeSize*StringToInt(Str(ptr));
  1041.     EXCEPT
  1042.     ELSE
  1043.       Terminate("Parameter error:",
  1044.               "Swapbuffer not valid",
  1045.               "Use 'SWAPBUFFER=...'",
  1046.               "In blocks of 8KBytes");
  1047.     END;
  1048.  
  1049.     ptr:=FindToolType(obj.toolTypes,"FILESIZE".data'PTR);
  1050.     IF ptr#NIL THEN
  1051.       TRY
  1052.         SwapSize:=NodeSize*16*StringToInt(Str(ptr));
  1053.       EXCEPT
  1054.       ELSE
  1055.         Terminate("Parameter error:",
  1056.                   "Swapsize not valid",
  1057.                   "Use 'FILESIZE=...'",
  1058.                   "In blocks of 128KBytes");
  1059.       END;
  1060.     END;
  1061.  
  1062.     ptr:=FindToolType(obj.toolTypes,"LOOKAHEAD".data'PTR);
  1063.     IF ptr#NIL THEN
  1064.       TRY
  1065.         LookAhead:=StringToInt(Str(ptr));
  1066.         IF LookAhead NOT OF 4..255 THEN
  1067.           Terminate("Parameter error:",
  1068.                   "Lookahead size",
  1069.                   "out of range",
  1070.                   "Use 'LOOKAHEAD=...'",
  1071.                   "between 4 and 255");
  1072.         END;
  1073.       EXCEPT
  1074.       ELSE
  1075.         Terminate("Parameter error:",
  1076.                 "Lookahead size not valid",
  1077.                 "Use 'LOOKAHEAD=...'",
  1078.                 "In blocks of 8KBytes");
  1079.       END;
  1080.     END;
  1081.  
  1082.     ptr:=FindToolType(obj.toolTypes,"VMEMBASE".data'PTR);
  1083.     IF ptr#NIL THEN
  1084.       TRY
  1085.         VMemBase:=HexStringToInt(Str(ptr));
  1086.         IF LookAhead NOT OF $08000000..LONGINT'MAX THEN
  1087.           Terminate("Parameter error:",
  1088.                     "VMem base",
  1089.                     "out of range",
  1090.                     "Use VMEMBASE=...'",
  1091.                     "between 08000000 and 7FFFFFFF");
  1092.         END;
  1093.       EXCEPT
  1094.       ELSE
  1095.         Terminate("Parameter error:",
  1096.                   "VMem base address not valid",
  1097.                   "Use 'VMEMBASE=...'",
  1098.                   "08000000 to 7FFFFFFF");
  1099.       END;
  1100.     END;
  1101.   ELSE
  1102.     Terminate("This is a WB programm");
  1103.   END;
  1104. END GetStartupParams;
  1105.  
  1106. BEGIN
  1107.   GetStartupParams;
  1108.  
  1109.   |
  1110.   | Dieser Test wirkt etwas gewagt, da er auch bei möglichen Spiegelungen
  1111.   | auf einen A3000 schließt. Dies dürfte aber keine Nachteile mit sich bringen,
  1112.   | da ja das Kickstart dann auf eine seiner Spiegelungen gemappt wird.
  1113.   |
  1114.   isA3000:=(LongPtr($F80000)^=LongPtr($7F80000)^) AND
  1115.            (LongPtr($F81234)^=LongPtr($7F81234)^) AND
  1116.            (LongPtr($FC0000)^=LongPtr($7FC0000)^) AND
  1117.            (LongPtr($FC0064)^=LongPtr($7FC0064)^);
  1118.   VBR:=GetVBR();
  1119.  
  1120.  
  1121.   oldIllegal[0]:=ProcPtr(VBR+$8)^;
  1122.   myA4[0]:=REG(A4);
  1123.  
  1124.   Exec.InitSemaphore(MemSem);
  1125.   ServerPort.sigBit:=32;
  1126.  
  1127.   DiskIO.Open(SwapFileName,SwapSize);
  1128.  
  1129.   Exec.Forbid;
  1130.   OldAllocMem[0]:=Exec.SetFunction(Exec.ExecBase,-198,SaveAllocMem);
  1131.   OldFreeMem[0] :=Exec.SetFunction(Exec.ExecBase,-210,SaveFreeMem);
  1132.   OldAvailMem[0]:=Exec.SetFunction(Exec.ExecBase,-216,SaveAvailMem);
  1133.   CacheClearU;
  1134.   Exec.Permit;
  1135.  
  1136.   Exec.FindTask(NIL).name:="Swapper".data'PTR;
  1137.  
  1138.   ServerPort.flags:=Exec.signal;
  1139.   ServerPort.sigBit:=Exec.AllocSignal(-1);
  1140.   ServerPort.sigTask:=Exec.ExecBase.thisTask;
  1141.   ServerPort.msgList.head:=Exec.NodePtr(ServerPort.msgList.tail'PTR);
  1142.   ServerPort.msgList.tail:=NIL;
  1143.   ServerPort.msgList.tailPred:=Exec.NodePtr(ServerPort.msgList.head'PTR);
  1144.  
  1145.   ProcPtr(VBR+$8)^:=BusError;
  1146.   GetMMUConfig(SysMMU);
  1147.   CreateMMUTree(OwnMMU);
  1148.   IF isA3000 THEN
  1149.     INCL(ShortPtr($DE0002)^,7);
  1150.   END;
  1151.   PutMMUConfig(OwnMMU);
  1152.  
  1153.   AddBuffers(PhysSize DIV NodeSize);
  1154.   CreateVMemArea(OwnMMU,VMemBase,VMemBase+SwapSize);
  1155.   FORGET Exec.SetTaskPri(Exec.FindTask(NIL),8);
  1156.   SwapMemIn(VMemBase);
  1157.   FORGET Exec.AddMemList(SwapSize,
  1158.                          Exec.MemReqSet:{Exec.fast,Exec.MemReqs(8)},
  1159.                          127,
  1160.                          VMemBase,"VMEM");
  1161.   Server;
  1162.  
  1163. CLOSE
  1164.   IF SysMMU.tc#MMUTCFlagSet:{} THEN
  1165.     PutMMUConfig(SysMMU);
  1166.   END;
  1167.   IF oldIllegal[0]#NIL THEN
  1168.     ProcPtr(VBR+$8)^:=oldIllegal[0];
  1169.   END;
  1170.   IF isA3000 THEN
  1171.     EXCL(ShortPtr($DE0002)^,7);
  1172.   END;
  1173.   IF ServerPort.sigBit#32 THEN
  1174.     Exec.FreeSignal(ServerPort.sigBit);
  1175.   END;
  1176. END VirtualMemory.
  1177.  
  1178.